"""
cdn.chord_api

This file defines the interface between cdn and the chord server.

@note: please don't use queryset.update(Server). this introduces bugs
that are hard to track down.
"""
### Start of imports

# This line just better work...
from jackson import debug

# There's debug stuff for the rest
debug.pre_import(__name__)

debug.importing(__name__, 'jackson.chord.client_api')
import jackson.chord.client_api as client_api

debug.importing(__name__, 'get_me from jackson.chord.db')
from jackson.chord.db import get_me

debug.importing(__name__, 'core, net from jackson.cdn')
from jackson.cdn import core, net

debug.importing(__name__, 'chord from jackson')
from jackson import chord

debug.importing(__name__, 'ext_json as json')
from jackson.extjson import ext_json as json

debug.post_import(__name__)

### End of imports

def add_key_range_handler(low, high, server):
    """ Relieves this server of responsibility for keys from low to high
    Updates state 'low' to new value, recovers any entries marked Outgoing or Delete
    @author: ep
    @param low: key at start of interval
    @param high: key at end of interval
    @param server: a chord.Server object representing the server who was responsible for these keys
    @rtype: None
    """
    context = [__name__, 'add_key_range_handler()']
    debug.enter(context, { 'low' : low, 'high' : high, 'previous_server' : server })
    debug.comment(context, 'i am now responsible for keys from ' + str(low)[:4] +"... to " + 
                                                                        str(high)[:4]+"... ")
    #@note: If all is well all additions will be at the bottom, except (possibly) the first one.
    if core.get_state('high') == core.get_state('low'):
        # we are the only server
        core.update_state('high', high)
    core.update_state('low', low)
    
    debug.comment(context, 'Collect any entries in the new key range', 3)
    # Collect any entries in the key range
    in_range_qs = core.get_content_in_key_range(low, high) 
    
    debug.comment(context, 'Recover outgoing content entries', 3)
    # Recover outgoing content entries
    in_range_qs.filter(status='O').update(status='H')
    
    debug.comment(context, 'Recover entries marked for deletion', 3)
    # Recover entries marked for deletion 
    in_range_qs.filter(status='D').update(status='T')
    
    debug.leave(context, { 'my_low' : str(core.get_state('low')), 'my_high' : str(core.get_state('high')) })


def del_key_range_handler(low, high, dest):
    """ Updates state 'low' to new value and prepares content to be transfered to dest.
    @author: ep
    @param low: key at start of interval
    @param high: key at end of interval
    @param server: a chord.Server object representing the server who is now responsible for these keys
    @rtype: None
    """
    context = [__name__, 'del_key_range_handler()', []]
    debug.enter(context, { 'low' : low, 'high' : high, 'newly_responsible_server' : dest })
    debug.comment(context, str({ 'my_low' : str(core.get_state('low')), 'my_high' : str(core.get_state('high')) }))
    #@note: If all is well, all deletions will be at the bottom, except (possibly) the first one.
    if low == high:
        # We don't need to do anything because we're killing the ring
        debug.leave(context, None, 2)
        return
    
    if core.get_state('high') == core.get_state('low'):
        # used if someone is joining a server responsible for everything
        core.update_state('high', low)
        core.update_state('low', high)
    else:
        # @change:3-15-09. this didn't make sense before.('low',low) previously
        core.update_state('low', high)
    
    # Lookup some info
    me = get_me()
    
    # Build a dictionary of affected keys and where to find the files
    # and update the content table
    dict = {}
    
    print "cdn.chord_api.del_key_range_handler(): getting query set of affected keys"
    # Construct a query set for these entries
    qs = core.get_content_in_key_range(low, high)
    print "qs:", len(qs)
    
    # Three cases:
    print "cdn.chord_api.del_key_range_handler(): processing incoming entries"
    # 1) Incoming entries: add entry.server to dict, redirect entry.server, delete the entry
    qs1 = qs.filter(status='I')
    for entry in qs1:
        dict[entry.key] = { "server" : entry.server, "md5_checksum" : entry.md5_checksum, "filename": entry.filename, "mimetype": entry.mimetype }
        net.redirect_key(entry.server, entry.key, dest)
    qs1.delete()
    
    print "cdn.chord_api.del_key_range_handler(): processing downloading entries"
    # 2) Transferring (downloading) entries: add, redirect, mark for deletion
    qs2 = qs.filter(status='T')
    for entry in qs2:
        dict[entry.key] = { "server" : entry.server, "md5_checksum" : entry.md5_checksum, "filename": entry.filename, "mimetype": entry.mimetype }
        net.redirect_key(entry.server, entry.key, dest)
    qs2.update(status='D')
    
    print "cdn.chord_api.del_key_range_handler(): processing outgoing entries"
    # 3) Here entries: add me to dict, change entries to Outgoing to dest
    qs3 = qs.filter(status='H')
    print "qs3:", len(qs3)
    print "cdn.chord_api.del_key_range_handler(): qs3 constructed"
    for entry in qs3:
        dict[entry.key] = { "server" : me, "md5_checksum" : entry.md5_checksum, "filename": entry.filename, "mimetype": entry.mimetype }
    print "cdn.chord_api.del_key_range_handler(): dictionary entries successful"
    print "cdn.chord_api.del_key_range_handler(): dest type = ", dest.__class__
    for entry in qs3:
        print "Type:", type(entry.server)
        print "From:", entry.server
        print "To: ", json.dumps(dest)
        entry.server = dest
        entry.save()
    print "cnd.chord_api.del_key_range_handler(): qs3 server updated"
    qs3.update(status='O')
    print "cdn.chord_api.del_key_range_handler(): qs3 status updated"
    print "cdn.chord_api.del_key_range_handler(): qs3 update successful"
    
    print "cdn.chord_api.del_key_range_handler(): sending package across the net with " + str(len(dict.keys())) + " keys"
    # Send this dictionary to dest
    net.delegate_keys(dest, dict)
    
    debug.leave(context,  { 'my_low' : str(core.get_state('low')), 'my_high' : str(core.get_state('high')) })
    return


def lookup(key):
    """ Talks to chord's client API to obtain the server responsible for the given key.
    @author: ep
    @param key: a key
    @rtype: chord.Server
    """
    return chord.client_api.lookup(key)

def register_event_handlers():
    """ **DEPRECATED** Subscribes particular functions to the chord AddKeyRange and DelKeyRange events.
    @author: ep
    @deprecated: just hardwired for now. generalizing this association is too clunky for our purposes.
    @todo: implement
    """
    pass
